home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1996 #15 / Monster Media Number 15 (Monster Media)(July 1996).ISO / netmail / rnr214.zip / EXEC23P.ZIP / EXEC.PAS < prev    next >
Pascal/Delphi Source File  |  1989-08-03  |  15KB  |  579 lines

  1. Unit exec;
  2. {
  3.    EXEC function with memory swap.
  4.    Needs Assembler file 'spawn.asm'.
  5.  
  6. Public domain software by
  7.  
  8.         Thomas Wagner
  9.         Ferrari electronic GmbH
  10.         Beusselstrasse 27
  11.         D-1000 Berlin 21
  12.         West Germany
  13.  
  14.         BIXname: twagner
  15. }
  16.  
  17. Interface
  18.  
  19. Uses
  20.   Dos;
  21.  
  22. type
  23.     filename = pathstr;
  24.     string128 = string [128];
  25.  
  26.  
  27. function do_exec (xfn: filename; pars: string128; spwn: integer;
  28.                   needed: word; newenv: boolean): integer;
  29.  
  30.    { The EXEC function.
  31.  
  32.       Parameters:    xfn   is a string containing the name of the file
  33.                            to be executed. If the string is empty,
  34.                            the COMSPEC environment variable is used to
  35.                            load a copy of COMMAND.COM or its equivalent.
  36.                            If the filename does not include a path, the
  37.                            current PATH is searched after the default.
  38.                            If the filename does not include an extension,
  39.                            the path is scanned for a COM or EXE file in
  40.                            that order.
  41.  
  42.                      pars  The program parameters.
  43.  
  44.                      spwn  If 1, the function will return, if necessary
  45.                            after swapping the memory image. 
  46.                            If -1, EMS will not be used during swapping.
  47.                            If 0, the function will terminate after the 
  48.                            EXECed program returns. 
  49.                            NOTE: If the program file is
  50.                            not found, the function will always return
  51.                            with the appropriate error code, even if 
  52.                            'spwn' is 0.
  53.  
  54.                      needed   The memory needed for the program in 
  55.                            paragraphs. If not enough memory is free, the
  56.                            program will be swapped out. Use 0 to never
  57.                            swap, $ffff to always swap. If 'spwn' is false,
  58.                            this parameter is irrelevant.
  59.  
  60.                      newenv   If this parameter is FALSE, the environment
  61.                            of the spawned program is a copy of the parent's
  62.                            environment. If it is TRUE, a new environment
  63.                            is created which includes the modifications from
  64.                            previous 'putenv' calls.
  65.  
  66.       Return value:
  67.                            $0000..00FF: The EXECed Program's return code
  68.                            (0..255 decimal)
  69.                            $0100:       Error writing swap file
  70.                            (256 decimal)
  71.                            $0200:       Program file not found
  72.                            (512 decimal)
  73.                            $03xx:       DOS-error-code xx calling EXEC
  74.                            (768..1023 decimal)
  75.                            $0400:       Error allocating environment buffer
  76.                            (1024 decimal)
  77. }
  78.  
  79.  
  80. procedure putenv (envvar: string);
  81. {  Adds a string to the environment. Note that the change to the 
  82.    environment is valid for an exec'ed process only, and only if you
  83.    set the 'newenv' parameter in do_exec to TRUE. }
  84.  
  85.  
  86. function envcount: integer;
  87. function envstr (index: integer): string;
  88. function getenv (envvar: string): string;
  89.  
  90. { Replacement functions for the environment handling functions in the
  91.   DOS unit. All three functions work exactly like their DOS-unit 
  92.   counterparts, except that they recognize the changes to the child
  93.   environment produced by 'putenv'. }
  94.  
  95.  
  96.  
  97. {===========================================================================}
  98.  
  99. Implementation
  100.  
  101. const
  102.    swap_filename = '$$AAAAAA.AAA';
  103.  
  104.     m_swapping        = $01;
  105.     m_use_ems        = $02;
  106.     m_creat_temp    = $04;
  107.     m_exec            = $80;
  108.  
  109. type
  110.    stringptr = ^string;
  111.    stringarray = array [0..10000] of stringptr;
  112.    stringarrptr = ^stringarray;
  113.    bytearray = array [0..30000] of byte;
  114.    bytearrayptr = ^bytearray;
  115.  
  116. var
  117.    envptr: stringarrptr;   { Pointer to the changed environment }
  118.    envcnt: integer;        { Count of environment strings }
  119.  
  120.  
  121. function do_spawn (method: byte;
  122.                    var swapfn; var xeqfn; var cmdtail; envlen: word;
  123.                    var env): integer; external;
  124. {$L spawn}
  125.  
  126.  
  127. { Environment routines }
  128.  
  129. function envcount: integer;
  130.  
  131.    { Returns count of strings in environment. }
  132.  
  133.    var
  134.       cnt: integer;
  135.    begin
  136.    if envptr = nil { If not yet changed }
  137.       then envcount := dos.envcount
  138.       else envcount := envcnt;
  139.    end;
  140.  
  141.  
  142. function envstr (index: integer): string;
  143.  
  144.    { Returns environment string 'index' }
  145.  
  146.    begin
  147.    if envptr = nil { If not yet changed }
  148.       then envstr := dos.envstr (index)
  149.       else if (index <= 0) or (index >= envcnt)
  150.       then envstr := ''
  151.       else if envptr^ [index - 1] = nil
  152.       then envstr := ''
  153.       else envstr := envptr^ [index - 1]^;
  154.    end;
  155.  
  156.  
  157. function name_eq (var n1, n2: string): boolean;
  158.  
  159.    { Compares search string 'n1' with environment string 'n2'.
  160.      Case is insignificant. }
  161.  
  162.    var
  163.       i: integer;
  164.       eq: boolean;
  165.    begin
  166.    i := 1;
  167.    eq := false;
  168.    while (i <= length (n1)) and (i <= length (n2)) and
  169.          (upcase (n1 [i]) = upcase (n2 [i])) do
  170.       i := i + 1;
  171.    name_eq := (i > length (n1)) and (i <= length (n2)) and (n2 [i] = '=');
  172.    end;
  173.  
  174.  
  175. function searchenv (var str: string): integer;
  176.  
  177.    { Search for environment string, returns index in 'envptr' array.
  178.      Assumes 'envptr' is not NIL. }
  179.  
  180.    var
  181.       idx: integer;
  182.       found: boolean;
  183.    begin
  184.    idx := 0;
  185.    found := false;
  186.  
  187.    while (idx < envcnt) and not found do
  188.       begin
  189.       if envptr^ [idx] <> nil
  190.          then found := name_eq (str, envptr^ [idx]^);
  191.       idx := idx + 1;
  192.       end;
  193.    if not found
  194.       then searchenv := -1
  195.       else searchenv := idx - 1;
  196.    end;
  197.  
  198.  
  199. function getenv (envvar: string): string;
  200.  
  201.    { Returns value of environment string specified by name. }
  202.  
  203.    var
  204.       strp: stringptr;
  205.       eq: integer;
  206.    begin
  207.    if envptr = nil { If not yet changed }
  208.       then getenv := dos.getenv (envvar)
  209.       else begin
  210.       eq := searchenv (envvar);
  211.       if eq < 0
  212.          then getenv := ''
  213.          else begin
  214.          strp := envptr^ [eq];
  215.          eq := pos ('=', strp^);
  216.          getenv := copy (strp^, eq + 1, length (strp^) - eq);
  217.          end;
  218.       end;
  219.    end;
  220.  
  221.  
  222. procedure init_envptr;
  223.  
  224.    { Initialise 'envptr' array. Called when 'putenv' is used for the 
  225.      first time. Copies all environment strings into heap storage,
  226.      and builds an array of pointers to this strings. }
  227.  
  228.    var
  229.       i: integer;
  230.       str: string [255];
  231.    begin
  232.    envcnt := dos.envcount;
  233.    getmem (envptr, envcnt * sizeof (stringptr));
  234.    if envptr = nil
  235.       then exit;
  236.    for i := 0 to envcnt - 1 do
  237.       begin
  238.       str := dos.envstr (i + 1);
  239.       getmem (envptr^ [i], length (str) + 1);
  240.       if envptr^ [i] <> nil
  241.          then envptr^ [i]^ := str;
  242.       end;
  243.    end;
  244.  
  245.  
  246. procedure putenv (envvar: string);
  247.  
  248.    { Adds the string 'envvar' to the environment, or changes the
  249.      environment string if the name is already present. }
  250.  
  251.    var
  252.       idx, eq: integer;
  253.       help: stringarrptr;
  254.    begin
  255.    if envptr = nil
  256.       then init_envptr;
  257.    if envptr = nil
  258.       then exit;
  259.  
  260.    eq := pos ('=', envvar);
  261.    if eq = 0
  262.       then exit;
  263.    for idx := 1 to eq do
  264.       envvar [idx] := upcase (envvar [idx]);
  265.  
  266.    idx := searchenv (envvar);
  267.    if idx >= 0
  268.       then begin
  269.       freemem (envptr^ [idx], length (envptr^ [idx]^) + 1);
  270.  
  271.       if eq >= length (envvar)
  272.          then envptr^ [idx] := nil
  273.          else begin
  274.          getmem (envptr^ [idx], length (envvar) + 1);
  275.          if envptr^ [idx] <> nil
  276.             then envptr^ [idx]^ := envvar;
  277.          end;
  278.       end
  279.       else if eq < length (envvar)
  280.       then begin
  281.       getmem (help, (envcnt + 1) * sizeof (stringptr));
  282.       if help = nil
  283.          then exit;
  284.       move (envptr^, help^, envcnt * sizeof (stringptr));
  285.       freemem (envptr, envcnt * sizeof (stringptr));
  286.       envptr := help;
  287.       getmem (envptr^ [envcnt], length (envvar) + 1);
  288.       if envptr^ [envcnt] <> nil
  289.          then envptr^ [envcnt]^ := envvar;
  290.       envcnt := envcnt + 1;
  291.       end;
  292.    end;
  293.  
  294.  
  295.  
  296. { Routines to search for files }
  297.  
  298.  
  299. function exists (fn: filename): boolean;
  300.    
  301.    { Returns TRUE if a file with name 'fn' exists. }
  302.  
  303.    var
  304.       s: searchrec;
  305.    begin
  306.    findfirst (fn, readonly or hidden or sysfile or archive, s);
  307.    exists := doserror = 0;
  308.    end { exists };
  309.  
  310.  
  311. function tryext (var fn: filename): boolean;
  312.  
  313.    { Try '.COM' and '.EXE' on current filename, modify filename if found. }
  314.  
  315.    var
  316.       found: boolean;
  317.    begin
  318.    found := exists (fn + '.COM');
  319.    if found
  320.       then fn := fn + '.COM'
  321.       else begin
  322.       found := exists (fn + '.EXE');
  323.       if found
  324.          then fn := fn + '.EXE'
  325.       end;
  326.    tryext := found;
  327.    end;
  328.  
  329.  
  330.  
  331. function findfile (var fn: filename): boolean;
  332.  
  333.    { Try to find the file 'fn' in the current path. Modifies the filename
  334.      accordingly. }
  335.  
  336.    var
  337.       path: string [255];
  338.       prfx: filename;
  339.       i, j: integer;
  340.       ext, found: boolean;
  341.    begin
  342.    if fn = ''
  343.       then fn := getenv ('COMSPEC');
  344.  
  345.    i := pos ('\', fn);
  346.    j := pos ('.', fn);
  347.    if (j < i) and (j > 0)
  348.       then begin
  349.       j := i;
  350.       while (j <= length (fn)) and (fn [j] <> '.') do
  351.          j := j + 1;
  352.       end;
  353.    if (j > 0) and (j = length (fn))
  354.       then fn [0] := pred (fn [0]);
  355.  
  356.    ext := (j > 0) and (j < length (fn));
  357.  
  358.    if (ext)
  359.       then found := exists (fn)
  360.       else found := tryext (fn);
  361.  
  362.    if not found and (i = 0)
  363.       then begin
  364.       path := getenv ('PATH');
  365.       i := 1;
  366.       while i <= length (path) do
  367.          begin
  368.          j := 0;
  369.          while (path [i] <> ';') and (i <= length (path)) do
  370.             begin
  371.             j := j + 1;
  372.             prfx [j] := path [i];
  373.             i := i + 1;
  374.             end;
  375.          i := i + 1;
  376.          if (j > 0)
  377.             then begin
  378.             j := j + 1;
  379.             prfx [j] := '\';
  380.             prfx [0] := chr (j);
  381.             prfx := prfx + fn;
  382.             if ext
  383.                then found := exists (prfx)
  384.                else found := tryext (prfx);
  385.             if found
  386.                then begin
  387.                fn := prfx;
  388.                i := 999;
  389.                end;
  390.             end;
  391.          end;
  392.       end;
  393.    findfile := found;
  394.    end; { findfile }
  395.  
  396.  
  397. procedure tempdir (var outfn: filename);
  398.  
  399.    { Set temporary file path.
  400.      Read "TMP/TEMP" environment. If empty or invalid, clear path.
  401.      If TEMP is drive or drive+backslash only, return TEMP.
  402.      Otherwise check if given path is a valid directory.
  403.      If so, add a backslash, else clear path.
  404.    }
  405.    var
  406.       drive: string [2];
  407.       dir: dirstr;
  408.       name: namestr;
  409.       ext: extstr;
  410.       f: file;
  411.       attr: word;
  412.       regs: registers;
  413.  
  414.    begin
  415.    outfn := getenv ('TMP');
  416.    if outfn = ''
  417.       then outfn := getenv ('TEMP');
  418.  
  419.    if outfn = ''
  420.       then exit;
  421.  
  422.    if outfn [length (outfn)] in ['\', '/']
  423.       then dec (outfn [0]);
  424.  
  425.    fsplit (outfn, dir, name, ext);
  426.    drive := '';
  427.    if length (dir) > 1
  428.       then if dir [2] = ':'
  429.          then begin
  430.          drive := dir [1] + ':';
  431.          delete (dir, 1, 2);
  432.          end;
  433.  
  434.    if drive <> ''
  435.       then begin
  436.       regs.ah := $1c;
  437.       regs.dl := ord (upcase (drive [1])) - ord ('A') + 1;
  438.       msdos (regs);
  439.       if regs.al = $ff
  440.          then begin
  441.          outfn := '';
  442.          exit;
  443.          end;
  444.       end;
  445.  
  446.    if name = ''
  447.       then begin
  448.       if dir <> ''
  449.          then outfn := ''
  450.          else outfn := drive + '\';
  451.       exit;
  452.       end;
  453.  
  454.    assign (f, outfn);
  455.    getfattr (f, attr);
  456.    if (doserror <> 0) or 
  457.       ((attr and directory) = 0) or 
  458.       ((attr and readonly) <> 0)
  459.       then outfn := ''
  460.       else outfn := outfn + '\';
  461.    end;
  462.  
  463.  
  464. function do_exec (xfn: filename; pars: string128; spwn: integer;
  465.                   needed: word; newenv: boolean): integer;
  466.    var
  467.       swapfn: filename;
  468.       avail: word;
  469.       regs: registers;
  470.       envlen, einx: word;
  471.       idx, len: integer;
  472.       envp: bytearrayptr;
  473.       method: byte;
  474.    begin
  475.  
  476.    { First, check if the file to execute exists. }
  477.  
  478.    if not findfile (xfn)
  479.       then begin
  480.       do_exec := $200;
  481.       exit;
  482.       end;
  483.  
  484.    { Now create a copy of the environment if the user wants it, and
  485.      if the environment has been changed. }
  486.  
  487.    envlen := 0;
  488.    if newenv and (envptr <> nil)
  489.       then begin
  490.       for idx := 0 to envcnt - 1 do
  491.          envlen := envlen + length (envptr^ [idx]^) + 1;
  492.       if envlen > 0
  493.          then begin
  494.          envlen := envlen + 1;
  495.          getmem (envp, envlen);
  496.          if envp = nil
  497.             then begin
  498.             do_exec := $400;
  499.             exit;
  500.             end;
  501.          einx := 0;
  502.          for idx := 0 to envcnt - 1 do
  503.             begin
  504.             len := length (envptr^ [idx]^);
  505.             move (envptr^ [idx]^ [1], envp^ [einx], len);
  506.             envp^ [einx + len] := 0;
  507.             einx := einx + len + 1;
  508.             end;
  509.          envp^ [einx] := 0;
  510.          end;
  511.       end;
  512.  
  513.    if spwn = 0
  514.       then method := m_exec    { Mark 'EXEC' function }
  515.       else begin
  516.  
  517.       { Determine amount of free memory }
  518.       with regs do
  519.          begin
  520.          ax := $4800;
  521.          bx := $ffff;
  522.          msdos (regs);
  523.          avail := regs.bx;
  524.          end;
  525.  
  526.       { No swapping if available memory > needed }
  527.  
  528.       if needed < avail
  529.          then method := 0
  530.          else begin
  531.  
  532.          { Swapping necessary, use 'TMP' or 'TEMP' environment variable
  533.            to determine swap file path if defined. }
  534.  
  535.          if spwn < 0
  536.             then method := m_swapping
  537.             else method := m_swapping or m_use_ems;
  538.  
  539.          tempdir (swapfn);
  540.  
  541.             if (dosversion and $ff) >= 3
  542.                 then method := method or m_creat_temp
  543.                 else begin
  544.                 swapfn := swapfn + swap_filename;
  545.              len := length (swapfn);
  546.              while exists (swapfn) do
  547.                 begin
  548.                 if (swapfn [len] >= 'Z')
  549.                    then len := len - 1;
  550.                 if (swapfn [len] = '.')
  551.                    then len := len - 1;
  552.                 swapfn [len] := succ (swapfn [len]);
  553.                 end;
  554.                 end;
  555.          swapfn [length (swapfn) + 1] := #0;
  556.          end;
  557.       end;
  558.  
  559.    { All set up, ready to go. }
  560.  
  561.    swapvectors;
  562.    do_exec := do_spawn (method, swapfn, xfn, pars, envlen, envp^);
  563.    swapvectors;
  564.  
  565.    { Free the environment buffer if it was allocated. }
  566.  
  567.    if envlen > 0
  568.       then freemem (envp, envlen);
  569.    end;
  570.  
  571.  
  572. { Initialisation for environment processing }
  573.  
  574. Begin
  575. envptr := nil;
  576. envcnt := 0;
  577. End.
  578.  
  579.